package cn.tb.ymcc.service.impl;

import cn.tb.ymcc.doc.CourseDoc;
import cn.tb.ymcc.dto.CourseSearchDto;
import cn.tb.ymcc.esmapper.HighlightResultMapper;
import cn.tb.ymcc.repository.CourseEsRepository;
import cn.tb.ymcc.result.PageList;
import cn.tb.ymcc.service.ICourseEsService;
import cn.tb.ymcc.vo.AggrsBucket;
import cn.tb.ymcc.vo.EsAggsPageList;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class CourseEsServiceImpl implements ICourseEsService {
    @Autowired
    private CourseEsRepository courseEsRepository;
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;
    @Autowired
    private HighlightResultMapper highlightResultMapper;

    @Override
    public void saveCourse(CourseDoc doc) {
        courseEsRepository.save(doc);

    }

    @Override
    public void deleteCourse(Long courseId) {
        courseEsRepository.deleteById(courseId);
    }

    /**
     * 根据条件搜索课程
     * 1.封装排序条件
     * 2.封装分页相关参数
     * 3.封装查询条件
     *      dsl查询 match
     *      dsl过滤 filter
     * 4.搜索ES
     * @param dto
     * @return
     */
    @Override
    public PageList<CourseDoc> search(CourseSearchDto dto) {
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        //封装排序条件===================================
        if(StringUtils.hasLength(dto.getSortField())){
            String sortField = null;
            switch (dto.getSortField().toLowerCase()){
                case "xl": sortField="saleCount";
                break;
                case "xp": sortField="onlineTime";
                break;
                case "pl": sortField="commentCount";
                break;
                case "jg": sortField="price";
                break;
                case "rq": sortField="viewCount";
                break;
                default: break;
            }
            if(sortField != null){
                if(StringUtils.hasLength(dto.getSortType())){
                    SortOrder sortOrder = dto.getSortType().equalsIgnoreCase("ASC") ? SortOrder.ASC : SortOrder.DESC;
                    builder.withSort(new FieldSortBuilder(sortField).order(sortOrder));
                }
            }
        }
        //封装分页相关参数===============================
        builder.withPageable(PageRequest.of(dto.getPage()-1, dto.getRows()));
        //封装查询条件==================================
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //DSL查询----------------
        if(StringUtils.hasLength(dto.getKeyword())){
            boolQuery.must(QueryBuilders.matchQuery("name",dto.getKeyword()));
        }
        //DSL过滤-----------------
        if(dto.getCourseTypeId() != null){
            boolQuery.filter(QueryBuilders.termQuery("courseTypeId",dto.getCourseTypeId()));
        }
        if(StringUtils.hasLength(dto.getGradeName())){
            boolQuery.filter(QueryBuilders.termQuery("gradeName",dto.getGradeName()));
        }
        if(StringUtils.hasLength(dto.getChargeName())){
            boolQuery.filter(QueryBuilders.termQuery("chargeName",dto.getChargeName()));
        }
        if(dto.getPriceMin() != null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(dto.getPriceMin()));
        }
        if(dto.getPriceMax() != null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(dto.getPriceMax()));
        }
        builder.withQuery(boolQuery);
        //高亮显示实现======================
        //1.设置高亮样式    2.处理隐射结果   3.使用映射器处理结果
        builder.withHighlightFields(new HighlightBuilder.Field("name")
                .preTags("<span style='color:deepPink'>").postTags("</span>"));

        //es聚合==============================
        //添加聚合条件
        //1.设置搜索条件
        //添加等级名字聚合
        builder.addAggregation(AggregationBuilders.terms("aggsGradeName").field("gradeName"));
        //添加收费规则聚合
        builder.addAggregation(AggregationBuilders.terms("aggsChargeName").field("chargeName"));

        //搜索ES========================================
        NativeSearchQuery build = builder.build();
        //Page<CourseDoc> page = courseEsRepository.search(build);
        AggregatedPage<CourseDoc> page = elasticsearchRestTemplate.queryForPage(build, CourseDoc.class, highlightResultMapper);

        //2.处理搜索结果成前端需要的数据结构-map集合
        //拿到聚合的结果，转为map
        Map<String, Aggregation> aggregationMap = page.getAggregations().asMap();
        ParsedStringTerms gradeName = (ParsedStringTerms)aggregationMap.get("aggsGradeName");
        ParsedStringTerms chargeName = (ParsedStringTerms)aggregationMap.get("aggsChargeName");
        //处理等级聚合结果
        List<AggrsBucket> gradeNamebuckets = new ArrayList<>();
        gradeName.getBuckets().forEach(bucket->{
            String key = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();
            AggrsBucket aggrsBucket = new AggrsBucket(key,docCount);
            gradeNamebuckets.add(aggrsBucket);
        });
        //处理收费聚合查询
        List<AggrsBucket> chargeNamebuckets = new ArrayList<>();
        chargeName.getBuckets().forEach(bucket->{
            String key = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();
            AggrsBucket aggrsBucket = new AggrsBucket(key,docCount);
            chargeNamebuckets.add(aggrsBucket);
        });
        HashMap<String, List<AggrsBucket>> map = new HashMap<>();
        map.put("aggsGradeName",gradeNamebuckets);
        map.put("aggsChargeName",chargeNamebuckets);

        //封装pageList,返回总条数和所有数据和聚合结果
        return new EsAggsPageList<>(page.getTotalElements(),page.getContent(),map);



    }
}
